#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

typedef struct __sem_t
{
	unsigned char count;
	int (*wait)(struct __sem_t * sem);
	int (*pend)(struct __sem_t * sem);
	int (*pend_nowait)(struct __sem_t * sem);
	int (*post)(struct __sem_t * sem);
}sem_t;

typedef struct 
{
	unsigned int rear;
	unsigned int front;
	unsigned int count;
	unsigned int maxsize;
	unsigned int flags;
	unsigned char *pbuffer;
	sem_t sem;
}queue_t;
#define UART0_RX_BUFFER_MAXSIZE 4096
unsigned char uart0_rx_buffer[UART0_RX_BUFFER_MAXSIZE];
static queue_t uart0_rx_buffer_q =
{
	.rear=0,
	.front=0,
	.count=0,
	.maxsize=UART0_RX_BUFFER_MAXSIZE,
	.flags=0,
	.pbuffer=uart0_rx_buffer,
};
inline int sem_wait(sem_t *sem)
{
	while(!sem->count);
	return 0;
}
inline  int sem_pend(sem_t *sem)
{
	int ret = 0;
	(!sem_wait(sem))?(sem->count--,ret=0):(ret = -1);	
	return ret;
}
inline int sem_pend_nowait(sem_t *sem)
{
	int ret;
	(sem->count)?(sem->count--,ret=0):(ret = -1);	
	return ret;
	
}
inline int sem_post(sem_t *sem)
{
	sem->count++;
	return 0;
}
void sem_init(sem_t *sem, unsigned char cnt)
{
	sem->count = cnt;
	sem->wait = sem_wait;
	sem->pend = sem_pend;
	sem->pend_nowait = sem_pend_nowait;
	sem->post = sem_post;
}

 int get_char(queue_t *q,unsigned char *pch)
{
	int result = 0;
	if(!q) return -1;
	if(q->front == q->rear)//buffer is empty
	{
		result = -1;
		goto out;
	}
	q->sem.pend(&q->sem);
	q->count--;
	q->sem.post(&q->sem);
	*pch = q->pbuffer[q->front];
	q->pbuffer[q->front] = 0;
	q->front = (q->front+1)%q->maxsize;
	
out:
	return result; 	
}

int put_char(queue_t *q,unsigned char ch)
{
	int result = 0;
	if(!q) return -1;
	
	if((q->rear+1)%q->maxsize == q->front)//buffer is full
	{
		result = -2;
		goto out;
	}
	q->sem.pend(&q->sem);
	q->count++;
	q->sem.post(&q->sem);
	
	q->pbuffer[q->rear] = ch;
	q->rear = (q->rear+1)%q->maxsize;
out:
	return result;
}

sem_t sem;


void *thread_function1 (void *arg);
void *thread_function2 (void *arg);

char message[] = "hello world!";
int
main ()
{
  int res;
  pthread_t a_thread;
  pthread_t b_thread;
  void *thread_result;

	sem_init(&uart0_rx_buffer_q.sem,0);
  res = pthread_create (&a_thread, NULL, thread_function1, (void *) message);
  if (res != 0)
    {
      perror ("thread creation failed!");
      exit (EXIT_FAILURE);
    }
  res = pthread_create (&b_thread, NULL, thread_function2, (void *) message);
  if (res != 0)
    {
      perror ("thread creation failed!");
      exit (EXIT_FAILURE);
    }
  printf ("waiting for thread to finish...\n");
  res = pthread_join (a_thread, &thread_result);//it will not return until other thread return!
  if (res != 0)
    {
      perror ("thread join failed!");
      exit (EXIT_FAILURE);
    }
  printf ("thread jioned, it returned %s\n", (char *) thread_result);
  printf ("message is now %s\n", message);
  exit (EXIT_SUCCESS);
}

void *thread_function1(void *arg)
{
	unsigned char i=0;
    printf("thread_function1 is running.Argument was %s\n", (char *)arg);
	while(1)
	{
		put_char(&uart0_rx_buffer_q,i++);
    	sleep(1);
	}
    strcpy(message, "Bye!");

    pthread_exit("Thank you for the cpu time!");
}
void *thread_function2(void *arg)
{
	unsigned char ch=0;
    printf("thread_function2 is running.Argument was %s\n", (char *)arg);
	while(1)
	{
		if(!get_char(&uart0_rx_buffer_q,&ch))
		{
		printf("ch=%d \n",ch);
		ch = 0;

		}
    	sleep(1);
	}
    strcpy(message, "Bye!");
    pthread_exit("Thank you for the cpu time!");
}
